#ifndef __WIZ_ALGEBRA_MATRIX_HPP__SHANHAOBO_19800429__
#define __WIZ_ALGEBRA_MATRIX_HPP__SHANHAOBO_19800429__

#include "./WizAlgebraMatrixCore.hpp"

namespace Wiz
{
    namespace Matrix
    {
        //////////////////////////////////////////////////////////////////////////
        template<typename ElementT, int NRow, int NCol>
        struct Type : public ::Wiz::Matrix::Core::Type<ElementT, NRow, NCol>
        {
            WIZ_DECLARE_CLASS_THIS(Type);
            ////////////////////////////////////////////////////////////////
            typedef ::Wiz::Matrix::Core::Type<ElementT, NRow, NCol>   tSuper;
            ////////////////////////////////////////////////////////////////

            ////////////////////////////////////////////////////////////////
            Type()
            {}
            Type(const tElement& InEle) : tSuper(InEle)
            {}
            Type(const tSuper& InMat) : tSuper(InMat)
            {}
            //////////////////////////////////////////////////////////////////////////

            ////////////////////////////////////////////////////////////////
            WIZ_INLINE ::Wiz::Void::Type GetRow(::Wiz::UInt::In Row, tRowVector& ov) const
            {
                WIZ_ASSERT(Row < eRowCnt);
                ::Wiz::Int::Type i;
                for (i = 0; i < eColCnt; i++)
                {
                    ov.e[i] = this->e[Row][i];
                }
            }
            WIZ_INLINE ::Wiz::Void::Type GetCol(::Wiz::UInt::In Col, tColVector& ov) const
            {
                WIZ_ASSERT(Col < eColCnt);
                ::Wiz::Int::Type i;
                for (i = 0; i < eRowCnt; i++)
                {
                    ov.e[i] = this->e[i][Col];
                }
            }

            WIZ_INLINE ::Wiz::Void::Type GetRow(::Wiz::UInt::In Row, ...) const
            {
                WIZ_ASSERT(Row < eRowCnt);
                va_list argptr;
                va_start(argptr, Row);
                ::Wiz::Int::Type i;
                for (i = 0; i < eColCnt; i++)
                {
                    va_arg(argptr, tElement&) = this->e[Row][i];
                }
                va_end(argptr); 
            }
            WIZ_INLINE ::Wiz::Void::Type GetCol(::Wiz::UInt::In Col, ...) const
            {
                WIZ_ASSERT(Col < eColCnt);
                va_list argptr;
                va_start(argptr, Row);
                ::Wiz::Int::Type i;
                for (i = 0; i < eRowCnt; i++)
                {
                    va_arg(argptr, tElement&) = this->e[i][Col];
                }
                va_end(argptr); 
            }

            /**/
            WIZ_INLINE ::Wiz::Void::Type SetRow(::Wiz::UInt::In Row, const tRowVector& iv)
            {
                WIZ_ASSERT(Row < eRowCnt);
                ::Wiz::Int::Type i;
                for (i = 0; i < eColCnt; i++)
                {
                    this->e[Row][i] = iv.e[i];
                }
            }
            WIZ_INLINE ::Wiz::Void::Type SetCol(::Wiz::UInt::In Col, const tColVector& iv)
            {
                WIZ_ASSERT(Col < eColCnt);
                ::Wiz::Int::Type i;
                for (i = 0; i < eRowCnt; i++)
                {
                    this->e[i][Col] = iv.e[i];
                }
            }

            WIZ_INLINE ::Wiz::Void::Type SetRow(::Wiz::UInt::In Row, ...)
            {
                WIZ_ASSERT(Row < eRowCnt);
                va_list argptr;
                va_start(argptr, Row);
                ::Wiz::Int::Type i;
                for (i = 0; i < eColCnt; i++)
                {
                    this->e[Row][i] = va_arg(argptr, const tElement&);
                }
                va_end(argptr); 
            }
            WIZ_INLINE ::Wiz::Void::Type SetCol(::Wiz::UInt::In Col, ...)
            {
                WIZ_ASSERT(Col < eColCnt);
                va_list argptr;
                va_start(argptr, Col);
                ::Wiz::Int::Type i;
                for (i = 0; i < eRowCnt; i++)
                {
                    this->e[i][Col] = va_arg(argptr, const tElement&);
                }
                va_end(argptr); 
            }
            ////////////////////////////////////////////////////////////////
            WIZ_INLINE tElement Determinant() const
            {
                return tSuper::Determinant<NRow, NCol>(*this);
            }
            ////////////////////////////////////////////////////////////////
            ::Wiz::Bool::Type Invert(tThis& OutMat) const
            {
                return tSuper::Invert(OutMat, *this);
            }
            ////////////////////////////////////////////////////////////////
            template<int rj, int ri>
            ::Wiz::Void::Type GetCofactor(tCofactor& C) const
            {
                tSuper::GetCofactor(C, *this, rj, ri);
            }
            ////////////////////////////////////////////////////////////////
            WIZ_INLINE tRowVector& operator[](::Wiz::UInt::In i)
            {
                return ::Wiz::Cast::Force<tRowVector>(this->v[i]);
            }
            WIZ_INLINE const tRowVector& operator[](::Wiz::UInt::In i) const
            {
                return ::Wiz::Cast::Force<const tRowVector>(this->v[i]);
            }
            ////////////////////////////////////////////////////////////////

            ////////////////////////////////////////////////////////////////
            WIZ_INLINE friend tThis operator+(tThisIn InMat1, tThisIn InMat2)
            {
                tThis OutMat;
                tSuper::Add(OutMat, InMat1, InMat2);
                return OutMat;
            }

            WIZ_INLINE friend tThis& operator+=(tThis& InOutMat, tThisIn InMat2)
            {
                return tSuper::AddAssign(InOutMat, InMat2);
            }

            WIZ_INLINE friend tThis operator-(tThisIn InMat1, tThisIn InMat2)
            {
                tThis OutMat;
                tSuper::Subtract(OutMat, InMat1, InMat2);
                return OutMat;
            }
            WIZ_INLINE friend tThis& operator-=(tThis& InOutMat, tThisIn InMat2)
            {
                return tSuper::SubtractAssign(InOutMat, InMat2);
            }

            WIZ_INLINE friend tThis operator*(tThisIn InMat1, tThisIn InMat2)
            {
                tThis Mat;
                tSuper::Multiply(Mat, InMat1, InMat2);
                return Mat;
            }
            WIZ_INLINE friend tThis& operator*=(tThis& InOutMat, tThisIn InMat2)
            {
                tThis Mat = InOutMat;
                tSuper::Multiply(InOutMat, Mat, InMat2);
                return InOutMat;
            }

            WIZ_INLINE friend tThis operator*(tThisIn InMat1, const tElement& InEle)
            {
                tThis Mat;
                tSuper::Multiply(Mat, InMat1, InEle);
                return Mat;
            }
            WIZ_INLINE friend tThis& operator*=(tThis& InOutMat, const tElement& InEle)
            {

                return tSuper::MultiplyAssign(InOutMat, InEle);
            }

            WIZ_INLINE friend tColVector operator*(tThisIn InMat, const tRowVector& InVec)
            {
                /// Row vector /// OGL
                tColVector Vec;
                tSuper::Multiply(Vec, InMat, InVec);
                return Vec;
            }
            WIZ_INLINE friend tRowVector operator*(const tColVector& InVec, tThisIn InMat)
            {
                /// Column vector /// D3D
                tRowVector Vec;
                tSuper::Multiply(Vec, InVec, InMat);
                return Vec;
            }
            ////////////////////////////////////////////////////////////////

            ////////////////////////////////////////////////////////////////
            WIZ_INLINE ::Wiz::Bool::Type operator==(tThisIn InMat) const
            {
                return tSuper::IsEqual(*this, InMat);
            }

            WIZ_INLINE ::Wiz::Bool::Type operator!=(tThisIn InMat) const
            {
                return tSuper::IsEqual(*this, InMat) == ::Wiz::Bool::False;
            }

            WIZ_INLINE tThisIn operator+() const
            {
                return *this;
            }

            WIZ_INLINE tThis operator-() const
            {
                tThis OutMat;

                ::Wiz::Size::Type i, j;

                for (j = 0; j < eRowCnt; j++)
                {
                    for (i = 0; i < eColCnt; i++)
                    {
                        OutMat.e[j][i] = -this->e[j][i];
                    }
                }

                return OutMat;
            }
            ////////////////////////////////////////////////////////////////
            WIZ_INLINE tThis& operator=(const tElement& InEle)
            {
                ::Wiz::Size::Type i, j;

                for (j = 0; j < eRowCnt; j++)
                {
                    for (i = 0; i < eColCnt; i++)
                    {
                        this->e[j][i] = InEle;
                    }
                }
                return *this;
            }

            WIZ_INLINE tThis& operator=(tThisIn InMat)
            {
                ::Wiz::Size::Type i, j;

                for (j = 0; j < eRowCnt; j++)
                {
                    for (i = 0; i < eColCnt; i++)
                    {
                        this->e[j][i] = InMat.e[j][i];
                    }
                }
                return *this;
            }
            ////////////////////////////////////////////////////////////////
        }; /// end of struct StdType
        ////////////////////////////////////////////////////////////////////
    } /// end of namespace Matrix
} /// end of namespace Wiz

#endif /*__WIZ_ALGEBRA_MATRIX_HPP__SHANHAOBO_19800429__*/
